package com.atguigu.gmall.order.service.impl;

import com.atguigu.gmall.common.constant.RabbitConstant;
import com.atguigu.gmall.common.constant.RedisConstant;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.AuthContextHolder;
import com.atguigu.gmall.common.util.JsonUtils;
import com.atguigu.gmall.feign.cart.CartFeignClient;
import com.atguigu.gmall.feign.product.SkuInfoFeignClient;
import com.atguigu.gmall.feign.ware.WareFeignClient;
import com.atguigu.gmall.model.bo.UserIdBo;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.model.to.mq.OrderCreateTo;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.Data;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author lfy
 * @description 针对表【order_info_1(订单表 订单表)】的数据库操作Service实现
 * @createDate 2022-03-21 16:49:50
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo>
        implements OrderInfoService {


    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    SkuInfoFeignClient skuInfoFeignClient;

    @Autowired
    WareFeignClient wareFeignClient;

    @Autowired
    CartFeignClient cartFeignClient;


    @Autowired
    OrderInfoService orderInfoService;

    @Resource
    OrderInfoMapper orderInfoMapper;

    @Autowired
    OrderDetailService orderDetailService;

    @Autowired
    RabbitTemplate rabbitTemplate;




    @Override
    public String generateOrderTradeNo(Long userId) {

        String token = UUID.randomUUID().toString().replace("-", "");
        //1、给redis再存一份
        //order:tradeno:1  =  79345734798275398247589324759
        String tokenKey = RedisConstant.ORDER_TRADENO_TOKEN + userId;
        //这个令牌只有10分钟的时间，10分钟以后，用户再提交数据，这个都算一次过期提交
        //过期令牌。假令牌，都给用户返回错误提示，让用户重新刷新页面即可
        redisTemplate.opsForValue().set(tokenKey, token, 10, TimeUnit.MINUTES);
        //2、返回给前端
        return token;
    }

    /**
     * 验证令牌
     *
     * @param token
     * @return
     */
    @Override
    public boolean checkUserTradenoToken(String token) {
        UserIdBo bo = AuthContextHolder.getUserIdBo();

        String tokenKey = RedisConstant.ORDER_TRADENO_TOKEN + bo.getUserId();

        //1、去redis验证这个token是否有，如果有就毁掉
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long status = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(tokenKey), token);
        return status == 1 ? true : false;
    }

    @Override
    public OrderInfo submitOrder(OrderInfo orderInfo, String tradeNo) {
        //1、验令牌
        boolean checked = checkUserTradenoToken(tradeNo);
        if (!checked) {
            //验证失败
            throw new GmallException(ResultCodeEnum.ORDER_TRADENO_ERROR);
        }


        //2、验价；  前端提交的价格不能作为订单真正存到数据库的价格
        //2.1）、前端提交来的每个商品价格，再去查一遍最新价格
        //2.2）、最新价格如果正好是前端提交来的价格，这个价格，也是订单最终存到数据库详情的价格
        //2.3）、最新价格如果不是前端提交来的价格，温馨提示前端，商品价格发生了变化，请重新刷新页面确认后再提交订单
        List<OrderDetail> list = orderInfo.getOrderDetailList();

        for (OrderDetail detail : list) {
            BigDecimal orderPrice = detail.getOrderPrice();
            BigDecimal price = skuInfoFeignClient.getSkuPrice(detail.getSkuId());
            if (!orderPrice.equals(price)) {
                //价格不相等
                throw new GmallException(ResultCodeEnum.PRODUCT_ORDER_PRICE_CHANGED);
            }
        }


        //不锁：
        //下单锁？恶意竞争导致的恶意锁定库存。
        //支付锁？我们支付扣库存。
        //3、验库存。
        for (OrderDetail detail : list) {
            String stock = wareFeignClient.checkStock(detail.getSkuId(), detail.getSkuNum());
            if (!"1".equals(stock)) {
                throw new GmallException(ResultCodeEnum.STOCK_NO);
            }
        }

        //4、保存订单
        OrderInfo bigSave = orderInfoService.orderBigSave(orderInfo);

        List<Long> skuIdsList = bigSave.getOrderDetailList().stream().map(item -> item.getSkuId()).collect(Collectors.toList());

        //5、清空购物车;
        cartFeignClient.clearCartItems(skuIdsList);

        //6、提交一个精准的延时任务
//        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
//        threadPool.schedule(()->{
//            orderInfoService.closeOrder(bigSave.getId());
//        },30,TimeUnit.MINUTES);

        //7、给队列发个Msg（orderId=8908098908,userId=1） 几us一个消息就发给MQ
        //orderId,userId
        String jsonMsg = JsonUtils.parseObjectToJsonStr(new OrderCreateTo(bigSave.getId(), bigSave.getUserId()));
        rabbitTemplate.convertAndSend(RabbitConstant.ORDER_EVENT_EXCHANGE,
                RabbitConstant.ORDER_CREATE_RK,
                jsonMsg);


        return bigSave;
    }

    //虽然分库分表，但是用user_id分的，不会涉及到跨库事务【分布式事务】
    //避免长事务
    @Transactional
    @Override
    public OrderInfo orderBigSave(OrderInfo orderInfo) {

        //1、保存订单信息
        OrderInfo preparedOrder = prepareOrderInfo(orderInfo);
        orderInfoMapper.insert(preparedOrder);


        //2、拿到订单id
//        Long orderId = preparedOrder.getId();

        //3、继续保存订单项数据
        orderDetailService.saveOrderDetail(preparedOrder);



        return preparedOrder;
    }

    @Override
    public void closeTimeoutOrder(Long orderId, Long userId) {
        //CAS 改状态
        orderInfoMapper.updateOrderStatus(orderId,userId,OrderStatus.UNPAID.name(),OrderStatus.CLOSED.name(),ProcessStatus.CLOSED.name());
    }

    @Override
    public OrderInfo getOrderById(Long orderId, Long userId) {
        QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("id",orderId);
        wrapper.eq("user_id",userId);
        return orderInfoMapper.selectOne(wrapper);
    }

    @Override
    public void updateOrderStatus(long userId, String trade_no, String orderStatus, String processStatus) {
        orderInfoMapper.updateOrderStatusByTrade_no(userId,trade_no,orderStatus,processStatus);
    }

    @Override
    public OrderInfo getOrderByOutTradeNo(String trade_no, long userId) {

        return  orderInfoMapper.getOrderAndDetailsByOutTradeNo(trade_no,userId);
    }

    @Override
    public List<Map<String, Object>> spiltSaveOrder(Long orderId, String wareSkuMap) {
        //wareSkuMap:   [{"wareId":"1","skuIds":["62"]},{"wareId":"2","skuIds":["61"]}]
        List<WareSkuMapBean> mapBeans = JsonUtils.parseJsonStrToObject(wareSkuMap, new TypeReference<List<WareSkuMapBean>>() {
        });

        List<Map<String, Object>> data = new ArrayList<>();
        for (WareSkuMapBean bean : mapBeans) {
            //每一个仓库分布都是一个子订单
            Map<String, Object> map = spiltBigOrderToSmallOrderAndSave(orderId, bean);
            data.add(map);
        }
        return data;
    }

    @Override
    public void updateOrderStatusById(Long id, Long userId, String orderStatus, String processStatus) {
        orderInfoMapper.updateOrderStatusById(id,userId,orderStatus,processStatus);

    }

    private Map<String,Object> spiltBigOrderToSmallOrderAndSave(Long orderId, WareSkuMapBean bean) {
        //1、查询原来的 orderId 的订单；
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);

        //2、拿到这个订单的所有商品信息
        QueryWrapper<OrderDetail> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",orderInfo.getUserId());
        wrapper.eq("order_id",orderId);
        List<OrderDetail> details = orderDetailService.list(wrapper);


        //拿到子单商品id集合
        Set<Long> skuIdSet = bean.getSkuIds().stream().map(item -> Long.parseLong(item)).collect(Collectors.toSet());
        String wareId = bean.getWareId();


        //3、获取当前子单对应的商品
        List<OrderDetail> childOrderDetail = details.stream().filter(item -> skuIdSet.contains(item.getSkuId())).collect(Collectors.toList());


        //4、准备子订单
        OrderInfo info = new OrderInfo();
        BeanUtils.copyProperties(orderInfo,info);

        info.setOrderDetailList(childOrderDetail);
        info.setId(null);
        info.setParentOrderId(orderId); //父订单id
        info.sumTotalAmount();
        info.setOrderStatus(OrderStatus.SPLIT.name());
        info.setProcessStatus(ProcessStatus.SPLIT.name());
        info.setWareId(wareId);//

        //5、保存子订单
        orderInfoMapper.insert(info);

        //6、子订单的所有商品的订单id改为子订单的id
        Long childOrderId = info.getId();
        List<OrderDetail> collect = childOrderDetail.stream()
                .map(item -> {
                    item.setOrderId(childOrderId); //现在这个商品的所属订单id就是子订单了
                    return item;
                }).collect(Collectors.toList());
        //7、保存数据
        orderDetailService.saveBatch(collect);


        //8、每个子订单存好以后给库存返回这个
        Map<String,Object> map = new HashMap<>();
        map.put("orderBody",info.getTradeBody());
        map.put("consignee",info.getConsignee());
        map.put("orderComment",info.getOrderComment());
        map.put("wareId",info.getWareId());
        map.put("orderId",info.getId());
        map.put("deliveryAddress",info.getDeliveryAddress());
        map.put("paymentWay","2");


        List<Map<String, Object>> list = childOrderDetail.stream().map(item -> {
            Map<String, Object> smallBean = new HashMap<>();
            smallBean.put("skuName", item.getSkuName());
            smallBean.put("skuId", item.getSkuId().toString());
            smallBean.put("skuNum", item.getSkuNum());
            return smallBean;
        }).collect(Collectors.toList());

        map.put("details",list);

        return map;

    }


    //准备订单数据
    private OrderInfo prepareOrderInfo(OrderInfo orderInfo) {
        UserIdBo bo = AuthContextHolder.getUserIdBo();

        OrderInfo info = new OrderInfo();
        //设置订单数据库字段的默认值
//        info.setId();  //由sharding-jdbc利用雪花算法生成
        info.setConsignee(orderInfo.getConsignee());
        info.setConsigneeTel(orderInfo.getConsigneeTel());

        //自动计算订单金额
        orderInfo.sumTotalAmount();
//        this.setTotalAmount(totalAmount);
//        this.setOriginalTotalAmount(originalTotalAmount);
//        this.setCouponAmount(couponAmount);
        info.setTotalAmount(orderInfo.getTotalAmount()); //订单总金额
        info.setOriginalTotalAmount(orderInfo.getOriginalTotalAmount());
        info.setCouponAmount(orderInfo.getCouponAmount());


        info.setOrderStatus(OrderStatus.UNPAID.name());  //UNPAID
        info.setUserId(bo.getUserId());
        info.setPaymentWay(orderInfo.getPaymentWay());
        info.setDeliveryAddress(orderInfo.getDeliveryAddress());
        info.setOrderComment(orderInfo.getOrderComment());

        //对外交易号  ATGUIGU-4824828402048204201
        info.setOutTradeNo("ATGUIGU-" + System.currentTimeMillis() + "-"+bo.getUserId());
        String tradeBody = getTradeBody(orderInfo);
        info.setTradeBody(tradeBody); //交易体
        Date date = new Date();
        info.setCreateTime(date); //订单创建时间

        long expire = date.getTime() + RedisConstant.ORDER_TIMEOUT; //订单30min过期
        info.setExpireTime(new Date(expire)); //订单过期时间；


        info.setProcessStatus(ProcessStatus.UNPAID.name()); //订单处理状态

        info.setTrackingNo(""); //物流追踪号

        info.setParentOrderId(0L);  //拆单

        info.setImgUrl(orderInfo.getOrderDetailList().get(0).getImgUrl());

        //订单买的所有商品
        info.setOrderDetailList(orderInfo.getOrderDetailList());


        info.setWareId("");  //分配给哪个仓库进行出库

        info.setProvinceId(0L);


        info.setActivityReduceAmount(new BigDecimal("0"));


        info.setRefundableTime(new Date()); //订单商品被签收后30天内可退款


        info.setFeightFee(new BigDecimal("0"));

        info.setOperateTime(new Date());


        return info;
    }


    //生成一个订单交易体（订单的支付提示内容）
    private String getTradeBody(OrderInfo orderInfo) {
        String address = "收货地址：" + orderInfo.getDeliveryAddress() + "  收货人：" + orderInfo.getConsignee() + " " + orderInfo.getConsigneeTel();
        String product = "商品名称：" + orderInfo.getOrderDetailList().get(0).getSkuName();
        return address + "<br/>" + product;
    }


    @Data
    static class WareSkuMapBean{
        //{"wareId":"1","skuIds":["62"]}
        private String wareId;
        private List<String> skuIds;
    }


}




